//
//  form.m
//  iCodeMacRun
//
//  Created by Andrew Barry on 26/03/09.
//  Copyright 2009 Barry Computer Services. All rights reserved.
//

#import "form.h"
#import "FlippedView.h"
#import "WrappedJsval.h"
#import "objc.h"

#ifdef MACOSX
@interface icmFormDelegate : NSObject {
	jsvalue owner;
	jscontext *_cx;
}
@end


@implementation icmFormDelegate
-(id) initWithObject: (jsvalue) obj withContext: (jscontext *) cx {
	_cx = cx;
	owner = obj;
	jsaddroot(cx, &owner);
	return self;
}

- (BOOL)respondsToSelector:(SEL)aSelector {
    if ( [super respondsToSelector:aSelector] )
        return YES;
    else {
		jsvalue param = jsstrFromCstring(_cx, sel_getName(aSelector));
		jsvalue rv = jsinvokename(_cx, owner, "_canRespondToSelector", &param, 1);
		if (rv == JSVAL_TRUE)
			return YES;
    }
    return NO;
}

/*
- (void)doCommandBySelector:(SEL)aSelector {
	if (aSelector == @selector(delete:)) {
		printf("delete it\n");
		return;
	}
	[super doCommandBySelector:aSelector];
}

*/
@end


jsvalue formFrameGetter(jscontext *cx, jsvalue obj, int sel) {
	NSView *view = idFromObject(cx, obj);
	NSRect frame = [view frame];
	double v = 0;
	switch (sel) {
		case 0:
			v = frame.origin.x;
			break;
		case 1:
			v = frame.origin.y;
			break;
		case 2:
			v = frame.size.width;
			break;
		case 3:
			v = frame.size.height;
			break;
	}
	return jsnum(cx, v);
}

jsvalue formInnerGetter(jscontext *cx, jsvalue obj, int sel) {
	NSView *view = idFromObject(cx, obj);
	NSRect frame = [[view contentView] frame];
	double v = 0;
	switch (sel) {
		case 4:
			v = frame.size.width;
			break;
		case 5:
			v = frame.size.height;
			break;
	}
	return jsnum(cx, v);
}

void formFrameSetter(jscontext *cx, jsvalue obj, int sel, jsvalue val) {
	NSWindow *view = idFromObject(cx, obj);
	NSRect frame = [view frame];
	double v = jsasnum(cx, val);

	switch (sel) {
		case 0:
			frame.origin.x = v;
			break;
		case 1:
			frame.origin.y = v;
			break;
		case 2:
			frame.size.width = v;
			break;
		case 3:
			frame.size.height = v;
			break;
	}
	[view setFrame:frame display: YES];
}

@interface SheetDelegate : NSObject {
	@public jsvalue scope, callback;
	jscontext *_cx;
}
@end

@implementation SheetDelegate
-(id) initWithContext: (jscontext *) cx {
	self = [super init];
	scope = INT_TO_JSVAL(0);
	callback = INT_TO_JSVAL(0);
	_cx = cx;
	jsaddroot(cx, &scope);
	jsaddroot(cx, &callback);
	return self;
}

-(void) dealloc {
	jsremoveroot(_cx, &scope);
	jsremoveroot(_cx, &callback);
	[super dealloc];
}

- (void)didEndSheet:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo {
	[sheet orderOut:self];
	
	if (JSVAL_IS_OBJECT(callback) && JSVAL_IS_OBJECT(scope)) {
		jsinvoke(_cx, callback, scope, nil, 0);
	}
}
@end

jsvalue formShowSheet(jscontext *cx, jsframe *outer, jsvalue obj, jsvalue *argv, int argc) {
	if (argc < 1)
		return JSVAL_VOID;
	if (!JSVAL_IS_OBJECT(argv[0]))
		return JSVAL_VOID;
	jsvalue sheetObj = argv[0];
	NSWindow *sheet = idFromObject(cx, sheetObj);
	NSWindow *wnd = idFromObject(cx, obj);

	SheetDelegate *delegate = [[SheetDelegate alloc] initWithContext:cx];

	if (argc >= 3) {
		delegate->scope = argv[1];
		delegate->callback = argv[2];
	}
	
	[NSApp beginSheet:sheet modalForWindow:wnd modalDelegate:delegate didEndSelector:@selector(didEndSheet:returnCode:contextInfo:) contextInfo:nil];
	jsset(cx, sheetObj, 0, "_openSheet", JSVAL_TRUE);
	return JSVAL_VOID;
}

jsvalue showWindowMethod(jscontext *cx, jsframe *outer, jsvalue obj, jsvalue *argv, int argc) {
	NSWindow *wnd = idFromObject(cx, obj);
	[wnd makeKeyAndOrderFront:wnd];
	return JSVAL_VOID;
}

jsvalue hideWindowMethod(jscontext *cx, jsframe *outer, jsvalue obj, jsvalue *argv, int argc) {
	NSWindow *wnd = idFromObject(cx, obj);
	[wnd orderOut:wnd];
	return JSVAL_VOID;
}

jsvalue closeWindowMethod(jscontext *cx, jsframe *outer, jsvalue obj, jsvalue *argv, int argc) {
	NSWindow *wnd = idFromObject(cx, obj);
	
	if (argc > 0) {
		jsset(cx, obj, 0, "closeReason", argv[0]);
	} else {
		jsdelete(cx, obj, 0, jsstrFromCstring(cx, "closeReason"));
	}
	
	jsvalue fv = jsget(cx, obj, 0, "_openSheet");
	if (fv) {
		if (jsasbool(cx, fv)) {
			[NSApp endSheet:wnd];
			jsdelete(cx, obj, 0, jsstrFromCstring(cx, "_openSheet"));
		}
	} else {
		[wnd close];
	}
	return JSVAL_VOID;
}

jsvalue formMoveMethod(jscontext *cx, jsframe *outer, jsvalue obj, jsvalue *argv, int argc) {
	NSWindow *view = idFromObject(cx, obj);
	if (argc < 2)
		return JSVAL_VOID;
	NSRect frame = [view frame];
	frame.origin.x = jsasnum(cx, argv[0]);
	frame.origin.y = jsasnum(cx, argv[1]);
	if (argc < 4) {
		frame.size.width = jsasnum(cx, argv[2]);
		frame.size.height = jsasnum(cx, argv[3]);
	}
	[view setFrame:frame display: YES];
	return JSVAL_VOID;
}

@interface AlertClose : NSObject {
}
-(void) errorSheetClose:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void *)contextInfo;
@end

@implementation AlertClose
-(void) errorSheetClose:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void *)contextInfo {
}
@end
#endif

jsvalue formAlertMethod(jscontext *cx, jsframe *outer, jsvalue obj, jsvalue *argv, int argc) {
	if (argc < 1)
		return JSVAL_VOID;

	NSString *str = jsasstrRetain(cx, argv[0]);
	NSString *heading = [@"Alert" retain];
	
	if (argc > 1)
		heading = jsasstrRetain(cx, argv[1]);

#ifdef MACOSX
	NSWindow *wnd = idFromObject(cx, obj);

	
	NSAlert *alert = [[NSAlert alloc] init];
	[alert setMessageText:heading];
	[alert setInformativeText:str];
	[alert addButtonWithTitle:@"Close"];
	[alert setAlertStyle:NSWarningAlertStyle];
	
	AlertClose *del = [AlertClose new];
	[alert beginSheetModalForWindow:wnd modalDelegate:del didEndSelector:@selector(errorSheetClose:returnCode:contextInfo:) contextInfo:nil];
#endif
#ifdef PHONEOSX
	UIAlertView *alert = [[UIAlertView alloc] initWithTitle:heading message:str delegate:nil cancelButtonTitle:@"Close" otherButtonTitles:nil];
	[alert show];
#endif
	
	[str release];
	[heading release];
	return JSVAL_VOID;
}

jsvalue formConstructor(jscontext *cx, jsframe *outer, jsvalue obj, jsvalue *argv, int argc) {
	return JSVAL_VOID;
}

static jsvalue handleCreateWindow(jscontext *cx, NSWindow *wnd, jsvalue obj, jsvalue defn) {
	NSRect wndRect = [wnd frame];

	NSRect mainScreen = [[NSScreen mainScreen] visibleFrame];
	[wnd setFrameOrigin:NSMakePoint(mainScreen.origin.x + 100, mainScreen.origin.y + mainScreen.size.height - 100 - wndRect.size.height)];
	
	NSString *autosave = jsgetstring(cx, defn, 0, "frameAutosave", @"");
	if (autosave && [autosave length])
		[wnd setFrameAutosaveName:autosave];
	
	[wnd setTitle:jsgetstring(cx, defn, 0, "title", @"Untitled")];
	
	FlippedView *content = [[FlippedView alloc] init];
	
	if (jsgetbool(cx, defn, 0, "hasBackgroundColor", false)) {
		NSString *strBackgroundColor = jsgetstring(cx, defn, 0, "title", @"backgroundColor");
		if (strBackgroundColor) {
			NSArray *bits = [strBackgroundColor componentsSeparatedByString: @"/"];
			float r = [[bits objectAtIndex:0] floatValue];
			float g = [[bits objectAtIndex:1] floatValue];
			float b = [[bits objectAtIndex:2] floatValue];
			NSColor *col = [NSColor colorWithCalibratedRed:r green:g blue: b alpha: 1.0f];
			[content setBackgroundColor:col];
		}
	}
	
	//	[content setWantsLayer:YES];
	[wnd setContentView:content];
	
	[wnd setDelegate:[[icmFormDelegate alloc] initWithObject: obj withContext: cx]];
	
	idToObject(cx, obj, wnd);
	
	jsset(cx, obj, 0, "config", defn);
	jsset(cx, obj, 0, "_handle", wrapCocoaObject(cx, wnd));
	
	return obj;
}

jsvalue formCreateWindow(jscontext *cx, jsframe *outer, jsvalue obj, jsvalue *argv, int argc) {
	jsvalue defn = argv[0];
	NSRect frame;
	
	int width =  jsgetint(cx, defn, 0, "width", 100);
	int height = jsgetint(cx, defn, 0, "height", 100);
	NSRect r;
	r = NSMakeRect(100, 100, width, height);
	int styles = NSTitledWindowMask;
	
	if (jsgetbool(cx, defn, 0, "canResize", false))
		styles |= NSResizableWindowMask;
	
	if (jsgetbool(cx, defn, 0, "canClose", false))
		styles |= NSClosableWindowMask;
	
	NSWindow *wnd = [[NSWindow alloc] initWithContentRect:r styleMask:styles backing:NSBackingStoreBuffered defer:NO];
	
	return handleCreateWindow(cx, wnd, obj, defn);
}

jsvalue formCreatePanel(jscontext *cx, jsframe *outer, jsvalue obj, jsvalue *argv, int argc) {
	jsvalue defn = argv[0];
	
	int width =  jsgetint(cx, defn, 0, "width", 100);
	int height = jsgetint(cx, defn, 0, "height", 100);
	NSRect r;
	r = NSMakeRect(100, 100, width, height);
	int styles = NSTitledWindowMask | NSUtilityWindowMask;
	
	if (jsgetbool(cx, defn, 0, "canResize", false))
		styles |= NSResizableWindowMask;
	
	if (jsgetbool(cx, defn, 0, "canClose", false))
		styles |= NSClosableWindowMask;
	
	NSWindow *wnd = [[NSPanel alloc] initWithContentRect:r styleMask:styles backing:NSBackingStoreBuffered defer:NO];
	
	return handleCreateWindow(cx, wnd, obj, defn);
}

jsvalue formCreateSheet(jscontext *cx, jsframe *outer, jsvalue obj, jsvalue *argv, int argc) {
	jsvalue defn = argv[0];
	
	int width =  jsgetint(cx, defn, 0, "width", 100);
	int height = jsgetint(cx, defn, 0, "height", 100);
	NSRect r;
	r = NSMakeRect(100, 100, width, height);
	int styles = NSTitledWindowMask;
	
	if (jsgetbool(cx, defn, 0, "canResize", false))
		styles |= NSResizableWindowMask;
	
	if (jsgetbool(cx, defn, 0, "canClose", false))
		styles |= NSClosableWindowMask;
	
	NSWindow *wnd = [[NSPanel alloc] initWithContentRect:r styleMask:styles backing:NSBackingStoreBuffered defer:NO];
	
	[wnd setTitle:jsgetstring(cx, defn, 0, "title", @"Untitled")];
	
	FlippedView *content = [[FlippedView alloc] init];
	
	if (jsgetbool(cx, defn, 0, "hasBackgroundColor", false)) {
		NSString *strBackgroundColor = jsgetstring(cx, defn, 0, "title", @"backgroundColor");
		if (strBackgroundColor) {
			NSArray *bits = [strBackgroundColor componentsSeparatedByString: @"/"];
			float r = [[bits objectAtIndex:0] floatValue];
			float g = [[bits objectAtIndex:1] floatValue];
			float b = [[bits objectAtIndex:2] floatValue];
			NSColor *col = [NSColor colorWithCalibratedRed:r green:g blue: b alpha: 1.0f];
			[content setBackgroundColor:col];
		}
	}
	
	//	[content setWantsLayer:YES];
	[wnd setContentView:content];
	
	idToObject(cx, obj, wnd);
	
	jsset(cx, obj, 0, "config", defn);
	
	return obj;
}

void registerForm(jscontext *cx) {
	jsvalue form;
	jsvalue UI = cx->slots[9];
	
	jsset(cx, UI, 0, "Form", form = bindFunction(cx, formConstructor));

	jsaddfield(cx, form, 0, "_createWindow", bindFunction(cx, formCreateWindow));
	jsaddfield(cx, form, 0, "_createSheet", bindFunction(cx, formCreateSheet));
	jsaddfield(cx, form, 0, "_createPanel", bindFunction(cx, formCreatePanel));
	jsaddfield(cx, form, 0, "show", bindFunction(cx, showWindowMethod));
	jsaddfield(cx, form, 0, "alert", bindFunction(cx, formAlertMethod));
	jsaddfield(cx, form, 0, "close", bindFunction(cx, closeWindowMethod));
	jsaddfield(cx, form, 0, "hide", bindFunction(cx, hideWindowMethod));
	jsaddfield(cx, form, 0, "_showSheet", bindFunction(cx, formShowSheet));
}

jsvalue bindForm(jscontext *cx, NSWindow *w) {
	WrappedJsval *ui = [WrappedJsval wrap: cx->slots[9] withContext: cx];
	WrappedJsval *form = [ui get:@"Form"];
	jsvalue wnd = jsnew(cx, form->val, nil, 0);
	idToObject(cx, wnd, w);
	return wnd;
}

/*
 -(void) errorSheetClose:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void *)contextInfo {
 }
 

 */

/*
#ifdef MACOSX
JSBool newWindowMethod(jscontext *cx, jsobject *obj, uintN argc, jsval *argv, jsval *rval) {
	NSDictionary *defn = idFromReservedSlot(obj, 0);
	FormLoader *loader = [FormLoader new];
	NSWindow *wnd = [loader newWindow:defn];
	jsobject *wndObj = [loader loadForm:defn into:wnd];
	*rval = OBJECT_TO_JSVAL(wndObj);
	[loader startForm: wndObj];
	return JS_TRUE;
}
#endif

JSPropertySpec formProps[] = {
#ifdef MACOSX
{"left", 0, JSPROP_ENUMERATE | JSPROP_SHARED, formFrameGetter, formFrameSetter },
{"top", 1, JSPROP_ENUMERATE | JSPROP_SHARED, formFrameGetter, formFrameSetter },
{"width", 2, JSPROP_ENUMERATE | JSPROP_SHARED, formFrameGetter, formFrameSetter },
{"height", 3, JSPROP_ENUMERATE | JSPROP_SHARED, formFrameGetter, formFrameSetter },
{"innerWidth", 4, JSPROP_ENUMERATE | JSPROP_SHARED, formInnerGetter, nil },
{"innerHeight", 5, JSPROP_ENUMERATE | JSPROP_SHARED, formInnerGetter, nil },
#endif
{0, 0, 0, 0, 0}
};

JSPropertySpec formDefinitionProps[] = {
{0, 0, 0, 0, 0}
};

JSFunctionSpec formFunctions[] = {
#ifdef MACOSX
{"move", formMoveMethod, 4, 0, 0 },
{"showSheet", formShowSheet, 1, 0, 0 },
{"show", showWindowMethod, 0, 0, 0 },
{"hide", hideWindowMethod, 0, 0, 0 },
{"close", closeWindowMethod, 0, 0, 0 },
#endif
{"alert", formAlertMethod, 1, 0, 0 },
{0, 0, 0, 0, 0}
};

JSFunctionSpec formDefinitionFunctions[] = {
#ifdef MACOSX
{"newWindow", newWindowMethod, 0, 0, 0 },
#endif
{0, 0, 0, 0, 0}

};
JSBool formConstructor(jscontext *cx, jsobject *obj, uintN argc, jsval *argv, jsval *rval) {
	printf("form construct\n");
	return JS_TRUE;
}

JSBool formDefinitionConstructor(jscontext *cx, jsobject *obj, uintN argc, jsval *argv, jsval *rval) {
	printf("definition construct\n");
	return JS_TRUE;
}

void formFinalizer(jscontext *cx, jsobject *obj) {
	printf("view finalize\n");
}

JSClass formClass = {"Form", JSCLASS_HAS_RESERVED_SLOTS(1), 
JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,JS_PropertyStub, 
JS_EnumerateStub,JS_ResolveStub,JS_ConvertStub,formFinalizer 
};

JSClass formDefinitionClass = {"FormDefinition", JSCLASS_HAS_RESERVED_SLOTS(1), 
JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,JS_PropertyStub, 
JS_EnumerateStub,JS_ResolveStub,JS_ConvertStub,formFinalizer 
};

jsobject *formObject, *formDefinitionObject;

void registerForm(jscontext *cx, jsobject *global) {
	JS_AddRoot(cx, &formObject);
	JS_AddRoot(cx, &formDefinitionObject);
	formObject = JS_InitClass(cx, global, NULL, &formClass,
								   formConstructor, 0,
								   formProps,
								   formFunctions, 
								   NULL, NULL);
	formDefinitionObject = JS_InitClass(cx, global, NULL, &formDefinitionClass,
							  formDefinitionConstructor, 0,
							  formDefinitionProps,
							  formDefinitionFunctions, 
							  NULL, NULL);
}

#ifdef MACOSX
jsobject *bindForm(jscontext *cx, NSWindow *wnd) {
#else
	jsobject *bindForm(jscontext *cx, UIView *wnd) {
#endif
	jsobject *obj = JS_NewObject(cx, &formClass, formObject, nil);
	jsval val;
	idToJSVal(wnd, &val);
	
	JS_SetReservedSlot(cx, obj, 0, val);
	return obj;
}

jsobject *bindFormDefinition(jscontext *cx, NSDictionary *defn) {
	jsobject *obj = JS_NewObject(cx, &formDefinitionClass, formDefinitionObject, nil);
	jsval val;
	idToJSVal(defn, &val);
	
	JS_SetReservedSlot(cx, obj, 0, val);
	return obj;
}

*/

